/*
 *  Sea.cpp
 *  Pirates!
 *
 *  Created by Alan Dorin on 20/1/11.
 *  Copyright 2007 __MyCompanyName__. All rights reserved.
 *
 */
 
using namespace std;

#include "Sea.h"

#include "ShipBase.h"
#include "GridCell.h"
#include "AIController.h"
#include "StudentAIControllerExample.h"
#include "Gold.h"

#include <iostream>
#include <fstream>
#include <vector>

extern long lrandBetween(long lowBound, long highBound);
extern void cleanQuit(char*);
extern Globals gGlobal;

// ---------------------------------------------------------------------------------------------------

Sea::Sea()
{
	cellArrayDimension	= 0;
	cellArray			= 0;
	cellArrayBuffer		= 0;
	
	shipArray			= 0;
	shipArrayBuffer		= 0;
	randomShipIndexArray= 0;
	numShips			= 0;
	
	numGolds			= 0;
	goldArray			= 0;
	
	numRocks			= 0;
}

// constructor to initiate the world with:
// randomly placed ships
// border and random central rocks
// random gold deposits

Sea::Sea(long newCellArrayDimension, long newNumShips)
{
	long a, b;
	SimpleVector tmpPosition, tmpDimensions;
	
	cellArrayDimension	= newCellArrayDimension;
	
	assert (cellArrayDimension>0);
	
	tmpDimensions.set(1/(double)cellArrayDimension, 1/(double)cellArrayDimension, 0);
	
	cellArray		= new GridCell* [cellArrayDimension];	// make an array of grid cells
	cellArrayBuffer	= new GridCell* [cellArrayDimension];	// make a buffer (for timestep t+1) of grid cells
	
	for (a=0; a<cellArrayDimension; a++)
	{
		cellArray[a]		= new GridCell [cellArrayDimension];	// make the grid cell array 2D
		cellArrayBuffer[a]	= new GridCell [cellArrayDimension];	// make the buffer grid cell array 2D
		
		for (b=0; b<cellArrayDimension; b++)						// set up the grid cells
		{															// don't set up the buffers. they'll be set up as the t+1 time step is calculated
			tmpPosition.set(a/(double)cellArrayDimension, b/(double)cellArrayDimension, 0);
			
			cellArray[a][b].setPosition(tmpPosition);
			cellArray[a][b].setDimensions(tmpDimensions);
			
			cellArrayBuffer[a][b].setPosition(tmpPosition);
			cellArrayBuffer[a][b].setDimensions(tmpDimensions);
		}
	}
	
	// ------------------
	// Set up the Rocks - MUST do this before placing the ships in the sea!
	
	long rockCount = 0;
	long rockX, rockY;
	numRocks = gGlobal.seaNumRocks;
	assert (numRocks < (cellArrayDimension*cellArrayDimension));
	assert (numRocks >= ((cellArrayDimension*4) - 4));	// rocks must at least stretch around the full boundary of the bay
	
	for (a=0; a<cellArrayDimension; a++)
	{
		for (b=0; b<cellArrayDimension; b++)
		{
			if ((a==0) || (b==0) || (a==cellArrayDimension-1) || (b==cellArrayDimension-1))	// boundary rocks
			{
				cellArray[a][b].setRock();
				cellArrayBuffer[a][b].setRock();
				rockCount++;
			}
		}
	}
	
	// Place the rest of the rocks in the middle of the sea
	// don't worry if two rocks are placed at the same place
	for ( ; rockCount<numRocks; rockCount++)
	{
		rockX = lrandBetween(1, cellArrayDimension-2);
		rockY = lrandBetween(1, cellArrayDimension-2);
		
		cellArray[rockX][rockY].setRock();
		cellArrayBuffer[rockX][rockY].setRock();
		rockCount++;
	}
	
	// ------------------
	// Set up the Gold deposits
	
	// number of gold deposits in this sea
	numGolds = gGlobal.seaNumGolds;
	assert (numGolds < (cellArrayDimension*cellArrayDimension));
	goldArray = new Gold [numGolds];
	
	for (a=0; a<numGolds; a++)
	{	goldArray[a].setIndex(a);	}
	
	// randomly distribute the gold in goldArray around the grid by setting their local positions
	randomiseGoldPositions();
	
	// There is no need to call placeGoldsInCellArrayAndCellArrayBuffer() here
	// because randomiseGoldPositions() places the gold in the cellArray and cellArrayBuffer buffer as they are positioned
	// i.e. it sets the grid pointers in the cellArray and cellArrayBuffer to point to the gold that have their respective x and y indices
	
	// ---------------
	// Set up the ships
	
	numShips		= newNumShips;
	shipArray		= new Ship [numShips];						// new the array of ships
	shipArrayBuffer	= new Ship [numShips];						// buffer to store the ship states at t+1	
	randomShipIndexArray = new long [numShips];
	
	// these will eventually hold controllers of the students...
	vector<AIController*> controllers(numShips);
	
	for (a=0; a<numShips; a++)
	{
		controllers[a] = new StudentAIControllerExample( );
	
		// set the controller's pointers to its own ship and the sea
		controllers[a]->setShipPtr(&shipArray[a]);
		controllers[a]->setSeaPtr(this);
	
		shipArray[a].setId(a);
		shipArray[a].setControllerPtr(controllers[a]);
	
		randomShipIndexArray[a] = a;	// to start with, order the random index list numerically 0..numShips for later shuffling
	}
	
	// then...
	// randomise the positions of all the ships in the GridCells with no collisions
	randomiseShipPositions();
	placeShipsInCellArray();
	
	outputSeaStatistics(numShips);
}

// copy constructor to initiate the world with copied Ships
Sea::Sea(const Sea& src)
{
	long a, b;
	
	cellArrayDimension = src.cellArrayDimension;
	
	if(src.cellArray)
	{
		cellArray		= new GridCell* [cellArrayDimension];	// make an array of grid cells
		cellArrayBuffer	= new GridCell* [cellArrayDimension];	// make an array of grid cells
	
		for (a=0; a<cellArrayDimension; a++)
		{
			cellArray[a]		= new GridCell [cellArrayDimension];
			cellArrayBuffer[a]	= new GridCell [cellArrayDimension];
			
			for (b=0; b<cellArrayDimension; b++)
			{
				// GridCell operator= will copy...
				// (i) the positions of the cells (which is wanted)
				cellArray[a][b] = src.cellArray[a][b];
				
				// (ii) the pointers to the ships in the src (which is not wanted)... so overwrite it.
				cellArray[a][b].clearShip();
				
				// (iii-a) the pointers to the golds in the src (which is not wanted)... so overwrite it.
				cellArray[a][b].clearGold();
				
				// (iii-b) in this case, the cellArray and its buffer both hold the same thing... i.e. pointers
				// to the same golds. Since the golds don't change between timesteps and they have no buffer
				// we can just treat the cellArrayBuffer's ptrs to golds the same as the cellArray's ptrs to golds.
				// Hence, clear them both and regenerate them below from a new goldArray.
				cellArrayBuffer[a][b].clearGold();
			}
		}
	}
	
	numShips = src.numShips;
	if(src.shipArray)
	{
		shipArray		= new Ship [numShips];	// make an array of Ships
		shipArrayBuffer	= new Ship [numShips];
		
		for (a=0; a<numShips; a++)
		{
			shipArray[a] = src.shipArray[a];				// copy the src Ship into the new Ship using the assignment operator
		}
	}
	
	if(src.randomShipIndexArray)
	{
		randomShipIndexArray = new long [numShips];
		
		for (a=0; a<numShips; a++)
		{
			randomShipIndexArray[a] = src.randomShipIndexArray[a];		// copy the src ShipIndexes into the new Ship indexes
		}
	}
	
	// Fill in the grid cells in the new copy with the ships copied from the src.
	// (This way we are sure to set the ship pointers to the new ships, not the src's ships)
	placeShipsInCellArray();
	
	// Copy across all the gold location and type data...
	numGolds = src.numGolds;
	
	if (src.goldArray)
	{
		goldArray = new Gold [numGolds];
		
		// make a copy of the array of golds
		for (a=0; a<numGolds; a++)
		{ goldArray[a] = src.goldArray[a]; }	// copy the src Gold into the new Gold using the assignment operator
	}
	
	// Fill in the grid cells in the new copy with the golds copied from the src.
	// (This way we are sure to set the gold pointers to the new golds, not the src's golds)
	placeGoldsInCellArrayAndCellArrayBuffer();
	
	numRocks = src.numRocks;
}

Sea::~Sea(void)
{
	delete [] shipArray;					// Delete the array of Ships
	delete [] shipArrayBuffer;
	delete [] randomShipIndexArray;
	
	for (long a=0; a<cellArrayDimension; a++)
	{
		delete [] cellArray[a];
		delete [] cellArrayBuffer[a];
	}
	
	delete [] cellArray;					// Delete the 2D array of GridCells
	delete [] cellArrayBuffer;
	delete [] goldArray;
}

void Sea::setShipPositionBuffer(long shipArrayIndex, long shipXPos, long shipYPos)
{
	assert (shipArrayIndex<numShips);
	assert (shipArrayIndex>=0);
	
	shipArrayBuffer[shipArrayIndex].setPosition(shipXPos, shipYPos, 0);
}

// Randomly set the ship positions in the shipArray through the cells around the sea
// Does NOT write to the cellArray but random positions are assigned
void Sea::randomiseShipPositions(void)
{
	long x=0, y=0, e=0;
    bool found=false;
	
	// generate a temp 2D array of Sea grid size
	bool** tempCollisionTestArray = new bool* [cellArrayDimension];
	for (long b=0; b<cellArrayDimension; b++)
	{
		tempCollisionTestArray[b] = new bool[cellArrayDimension];
	}
	
	// clear the temp array
	for (x=0; x<cellArrayDimension; x++)
	{
		for (y=0; y<cellArrayDimension; y++)
		{	tempCollisionTestArray[x][y] = false;	}
	}
    
    // for each element in the ship list....
    for (e=0; e<numShips; e++)
    {
        // find a random unoccupied cell...
        found = false;
        while (found==false)
        {
            x = lrandBetween(0, cellArrayDimension-1);
            y = lrandBetween(0, cellArrayDimension-1);
            
            if ((tempCollisionTestArray[x][y] == false) && (!cellArray[x][y].isOccupiedByRock()))
            {
                shipArray[e].setPosition(x, y, 0);			// and locate the element there.
				tempCollisionTestArray[x][y] = true;
                found = true;
            }
        }
	}
	
	// delete the temp array of Sea grid size
	for (long b=0; b<cellArrayDimension; b++)
	{
		delete [] tempCollisionTestArray[b];
	}
	delete [] tempCollisionTestArray;

}

bool Sea::placeShipsInCellArray(void)
{
	long e,x,y;
	
	assert(cellArray!=0);
	assert(shipArray!=0);
	
	// for each element in the ship array....
	for (e=0; e<numShips; e++)
    {
		x = shipArray[e].getPositionX();
		y = shipArray[e].getPositionY();
		
		if (cellArray[x][y].isOccupiedByShip() == false)
		{
			cellArray[x][y].setShipPtr(&shipArray[e]);
		}
		
		else
		{
			cerr << "\nWARN: (Sea::placeShipsInCellArray) failed attempt to place ships (ship list contains collisions in the cellArray)!";
			return false;
		}
	}
	return true;
}

bool Sea::placeBufferShipsInBufferCellArray(void)
{
	long e,x,y;
	
	assert(cellArrayBuffer!=0);
	assert(shipArrayBuffer!=0);
	
	// for each element in the ship array....
	for (e=0; e<numShips; e++)
    {
		x = shipArrayBuffer[e].getPositionX();
		y = shipArrayBuffer[e].getPositionY();
		
		if (cellArrayBuffer[x][y].isOccupiedByShip() == false)
		{
			cellArrayBuffer[x][y].setShipPtr(&shipArray[e]);
		}
		
		else
		{
			cerr << "\nWARN: (Sea::placeBufferShipsInBufferCellArray) failed attempt to place ships ";
			cerr << "(shipBuffer list contains collisions in the cellArrayBuffer)!\ncur shipId=" << e << " x=" << x << " y=" << y;
			cerr << " existingShipId=" << cellArrayBuffer[x][y].getShipId();
			return false;
		}
	}
	return true;
}


bool Sea::placeBufferShipInBufferCellArray(long shipArrayIndex)
{
	assert(cellArrayBuffer!=0);
	assert(shipArrayBuffer!=0);
	assert(shipArrayIndex<numShips);
	
	// get the current position for placement in the *future* buffer
	long x = shipArrayBuffer[shipArrayIndex].getPositionX();
	long y = shipArrayBuffer[shipArrayIndex].getPositionY();
	
	// check if the current position in the *future* buffer is available
	if (cellArrayBuffer[x][y].isOccupiedByShip() == false)
	{
		// if it's available, fill the ptr in the future buffer with the
		// ship from the *future* ship array. (Should this be the future shipArray)?
		cellArrayBuffer[x][y].setShipPtr(&shipArrayBuffer[shipArrayIndex]);
	}
		
	else
	{
		cerr << "\nWARN: (Sea::placeBufferShipInBufferCellArray) failed attempt to place ships ";
		cerr << "(shipBuffer list contains collisions in the cellArrayBuffer)!\ncur shipId=" << shipArrayIndex << " x=" << x << " y=" << y;
		cerr << " existingShipId=" << cellArrayBuffer[x][y].getShipId();
		return false;
	}
	
	return true;
}

// ----------------------------------------------------------------------------
// This is the "big" update function that runs through the game world, updating
// all of the ships and the presence of gold in the sea.
// Return false when all ships have been sunk (this is one game-over scenario)

bool Sea::update(void)
{
	long shipArrayIndex=0;
	long damageSustained = 0;
	long numSunkShips = 0;
	
	bool actionSuccessFlag=false;
	// create a temporary array to store all of the ships' requested actions.
	Globals::ShipAction* requestedAction = new Globals::ShipAction [numShips];
	
	// Copy the newly moved ships from the shipArray into the shipArrayBuffer
	// to allow us to set their future positions in updateShipPositions().
	for (shipArrayIndex=0; shipArrayIndex<numShips; shipArrayIndex++)
	{
		shipArrayBuffer[shipArrayIndex] = shipArray[shipArrayIndex];
		
		// clear the ship's damage flags before the new update
		shipArrayBuffer[shipArrayIndex].clearJustDamagedFlag();
	}
	
	// clear future state ship presence and cannonBallPassedThrough
	// to allow us to detect any future collisions that need to be avoided and redo the cannon ball paths
	// as we step through the ship's intended movements
	clearCellArrayBuffer();
	
	// Step through each ship (in random order where required)
	// the shipArray holds the current ship states.
	// the new (future) ship states should be written to the shipArrayBuffer
	
	for (shipArrayIndex=0; shipArrayIndex<numShips; shipArrayIndex++)
	{
		if (shipArray[shipArrayIndex].isSunk())
		{
			numSunkShips++;
		
			// this ship will sit here being a sunken wreck doing no actions and not being updated
			// always force this ship to have no action, don't even call its computeNextFunction()
			requestedAction[shipArrayIndex] = Globals::actionPass;
			
			// don't set the gridCell's shipPtr pointing to this ship in the cellArrayBuffer
			// but do set the cellArrayBuffers Wreck flag...
			cellArrayBuffer[shipArray[shipArrayIndex].getPositionX()][shipArray[shipArrayIndex].getPositionY()].setWreck();
		}
		
		else
		{
			// collect all the *requested* actions from the ships by calling their computeNextAction() functions
			requestedAction[shipArrayIndex] = shipArray[shipArrayIndex].computeNextAction();
		}
		
		// after calling the computeNextAction() or setting actionPass on the ship in shipArray, we need to
		// set the state in the shipBuffer array to record what was requested
		shipArrayBuffer[shipArrayIndex].setRequestedAction(requestedAction[shipArrayIndex]);
	}
	
	if (numSunkShips == numShips)
	{
		// delete the temporary array
		delete [] requestedAction;
		
		return false;	// game is over!
	}
	
	// This first loop must update all the ships that want to stay still during this time
	// step so that they have priority over their positions in the future state buffer.
	// Only when the stationary (turning, colecting gold or shooting) ships have been allocated their positions
	// for the next time step can we proceed to allow other ships to move...
	
	// This loop is to update stationary ships (doesn't need to be random order)
	for (shipArrayIndex=0; shipArrayIndex<numShips; shipArrayIndex++)
	{
		if (!shipArray[shipArrayIndex].isSunk())	// only update un-sunk ships
		{
			switch (requestedAction[shipArrayIndex])
			{
				case Globals::actionMoveAhead:
					; // do nothing with ships that want to move in this loop! (see note above loop)
				break;
				
				case Globals::actionTurnLeft:
				case Globals::actionTurnRight:
					actionSuccessFlag = attemptShipTurn(shipArrayIndex, requestedAction[shipArrayIndex]);
					shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				case Globals::actionPass:
					actionSuccessFlag = attemptShipPass(shipArrayIndex);
					shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				case Globals::actionCollectGold:
					actionSuccessFlag = attemptShipCollectGold(shipArrayIndex);
					shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				case Globals::actionFireCannonAhead:
					// at this stage of the update, don't actually fire the cannon (we will do that after
					// all ships have been positioned for the next time step in the cellArrayBuffer).
					// for now, just do a "actionPass" which makes this ship stationary and
					// enters its position in the cellArrayBuffer).
					actionSuccessFlag = attemptShipPass(shipArrayIndex);
					// ignore actionSuccessFlag, that will be set while firing cannon
					
					//actionSuccessFlag = attemptShipFireCannonAhead(shipArrayIndex); 
					//shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				default:
					cerr << "\n Sea::update(): invalid action (" << requestedAction[shipArrayIndex] << ") requested by ship:" << shipArrayIndex;
			}
		}
	}
	
	// This loop is to update ships that want to move (needs to be done in random order)
	// Shuffle the order since the last time step update...
	shuffleRandomShipIndexArray();
	
	// ...then proceed through the correct number of ships, but using the shuffled
	// indices to do the update of the ships in the shipArray.
	for (long s=0; s<numShips; s++)
	{
		shipArrayIndex = getRandomShipIndex(s);		// get the next *randomised* ship index
		
		if (!shipArray[shipArrayIndex].isSunk())	// only update un-sunk ships
		{
			damageSustained = 0;
			
			switch (requestedAction[shipArrayIndex])
			{
				case Globals::actionMoveAhead:
					actionSuccessFlag = attemptShipMoveAhead(shipArrayIndex);
					if (actionSuccessFlag == false)
					{
						// cerr << "\nShip " << shipArrayIndex << " sustained damage from attempt to move 1 step ahead!"; 
						shipArrayBuffer[shipArrayIndex].applyDamage(1);
					}
					shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				case Globals::actionTurnLeft:
				case Globals::actionTurnRight:
				case Globals::actionCollectGold:
				case Globals::actionFireCannonAhead:
				case Globals::actionPass:
					;	// do nothing with ships that want to remain stationary in this loop
						// these were handled in the first loop above (see note above uppermost loop)
				break;
				
				default:
					cerr << "\n Sea::update(): invalid action (" << requestedAction[shipArrayIndex] << ") requested by ship:" << shipArrayIndex;
			}
		}
	}
	
	// Now all ships have been made stationary or moved so everything is stored in its position
	// in the cellArrayBuffer. Now we can fire the cannons to see what is hit.
	for (shipArrayIndex=0; shipArrayIndex<numShips; shipArrayIndex++)
	{
		if (!shipArray[shipArrayIndex].isSunk())	// only update un-sunk ships
		{
			switch (requestedAction[shipArrayIndex])
			{
				case Globals::actionMoveAhead:
				case Globals::actionTurnLeft:
				case Globals::actionTurnRight:
				case Globals::actionCollectGold:
				case Globals::actionPass:
					;	// do nothing with ships that want to remain stationary or move in this loop
						// these were handled in the first two loops above (see note above uppermost loop)
				break;
			
				case Globals::actionFireCannonAhead:
					actionSuccessFlag = attemptShipFireCannonAhead(shipArrayIndex); 
					shipArrayBuffer[shipArrayIndex].setRequestedActionSuccessFlag(actionSuccessFlag);
				break;
				
				default:
					cerr << "\n Sea::update(): invalid action (" << requestedAction[shipArrayIndex] << ") requested by ship:" << shipArrayIndex;
			}
		}
	}
	
	// swap future state with present state
	swapArrayBuffers();
	
	// delete the temporary array
	delete [] requestedAction;
	
	// game is not yet over
	return true;
}

bool Sea::attemptShipPass(long shipArrayIndex)
{
	bool actionSuccessFlag=false;
	
	if (shipArray[shipArrayIndex].checkStationary())
	{
		// this call places the *buffer* copy of the ship in its new (same as the old) position into the *buffer* cell array
		shipArray[shipArrayIndex].makeStationary();
		actionSuccessFlag = true;
	}

	else
	{
		cleanQuit("ERROR: Sea::attemptShipPass() - ship can't stay still due to collision!");
	}
	
	return actionSuccessFlag;
}

bool Sea::attemptShipFireCannonAhead(long shipArrayIndex)
{
	extern long getAdjacentIndexX(Globals::NeighbourDirection direction, long curIndex, long xDimension);
	extern long getAdjacentIndexY(Globals::NeighbourDirection direction, long curIndex, long yDimension);
	
	bool actionSuccessFlag=false;
	long shipX = shipArray[shipArrayIndex].getPositionX();
	long shipY = shipArray[shipArrayIndex].getPositionY();
	Globals::NeighbourDirection ballDirection = shipArray[shipArrayIndex].getAheadDirection();
	long curBallX=shipX, curBallY=shipY;	// initally the ball is at the ship
	long targetShipIndex=0;
	
	// for each grid cell the cannon ball passes through up to its range
	for (long b=0; b<shipArray[shipArrayIndex].getCannonRange(); b++)
	{
		// indentify the cell the ball is moving through
		curBallX = getAdjacentIndexX(ballDirection, curBallX, cellArrayDimension);
		curBallY = getAdjacentIndexY(ballDirection, curBallY, cellArrayDimension);
		// std::cerr << "\nSea::attemptShipFireCannonAhead into cell [" << curBallX << ", " << curBallY << "]";
		
		// tag the cell in the future state buffer as having had a cannon ball through it
		cellArrayBuffer[curBallX][curBallY].setCannonBallPassedThrough(true);
		
		// if cell contains a ship in the *future* shipArray
		if (cellArrayBuffer[curBallX][curBallY].isOccupiedByShip())
		{
			targetShipIndex = cellArrayBuffer[curBallX][curBallY].getShipId();
			// std::cerr << " Hit target=" << targetShipIndex;

			// apply damage to the ship in the shipArrayBuffer (future state)
			shipArrayBuffer[targetShipIndex].applyDamage(1);
				
			actionSuccessFlag = true;
			break;	// cannon ball stops with first ship it hits
		}
		
		else if (cellArrayBuffer[curBallX][curBallY].isOccupiedByRock())	// cell contains a rock
		{	break; }														// cannon ball stops with first rock it hits
	}
	
	// Firing the cannon involves staying stationary for the time step
	// but this is already handled in the Sea::update() loop where the ship is *separately*
	// made stationary and entered into the right position in the future state.
	// Then, having already been made stationary, it fires its cannon.
	// ...so no need to call makeStationary() here
	
	return actionSuccessFlag;
}

bool Sea::attemptShipTurn(long shipArrayIndex, Globals::ShipAction requestedAction)
{
	extern Globals::NeighbourDirection getClockwiseNeighbourDirection(Globals::NeighbourDirection currentDirection);
	extern Globals::NeighbourDirection getAnticlockwiseNeighbourDirection(Globals::NeighbourDirection currentDirection);
	
	Globals::NeighbourDirection aheadDirection = shipArray[shipArrayIndex].getAheadDirection();
	Globals::NeighbourDirection nextAheadDirection = Globals::neighbourUp;
	
	// First check it is OK for this ship to remain stationary.
	// Ships staying stationary should have priority update in Sea->update(), so if this ship
	// can't turn here, something has gone wrong!
	if (shipArray[shipArrayIndex].checkStationary())
	{
		switch (requestedAction)
		{
			case Globals::actionTurnLeft:
				nextAheadDirection = getAnticlockwiseNeighbourDirection(aheadDirection);
			break;
			
			case Globals::actionTurnRight:
				nextAheadDirection = getClockwiseNeighbourDirection(aheadDirection);
			break;
			
			default:
				cleanQuit("ERROR: (Sea::attemptShipTurn()) Invalid switch option!");
		}
		
		// this call places the *buffer* copy of the ship in its new position into the *buffer* cell array
		shipArray[shipArrayIndex].makeStationary();
		
		// this call updates the heading of the ship stored in the *buffer* since this will
		// be the copy of the ship used in the *next* time step.
		shipArrayBuffer[shipArrayIndex].setAheadDirection(nextAheadDirection);
		
		return true;
	}
	
	else
	{
		cleanQuit("ERROR: Sea::attemptShipTurn() - ship can't stay still as it wanted to!");
	}
	
	return false;
}

bool Sea::attemptShipMoveAhead(long shipArrayIndex)
{
	assert(shipArrayIndex>=0);
	assert(shipArrayIndex<numShips);
	
	bool moveSucceeded=false;
	bool stationarySucceeded=false;
	
	Globals::NeighbourDirection aheadDirection = shipArray[shipArrayIndex].getAheadDirection();

	// check and if possible, move in the ahead direction one step
	
	if(shipArray[shipArrayIndex].checkMoveDirection(aheadDirection))
	{
		// this call places the *buffer* copy of the ship in its new position into the *buffer* cell array
		shipArray[shipArrayIndex].makeMove(aheadDirection);	// step ahead once
		moveSucceeded = true;
	}
	
	// else (if the ship couldn't move as it requested)
	{
		// leave flag at its default value (moveSucceeded = false)
	}

	// Try to stay still if moveSucceeded==false
	if (moveSucceeded == false)
	{
		if (shipArray[shipArrayIndex].checkStationary())
		{
			// this call places the *buffer* copy of the ship in its new (same as the old) position into the *buffer* cell array
			shipArray[shipArrayIndex].makeStationary();
			stationarySucceeded = true;
		}
	
		else
		{
			stationarySucceeded = false;
		}
	}
	
	if (moveSucceeded==true) // this move was all good!
	{	return true;	}
	
	else if ((moveSucceeded==false) && (stationarySucceeded==true))	// this move failed, a collision occurred, but staying still is ok.
	{	return false;	}
	
	else if (stationarySucceeded == false)	// this move was no good, and nor is staying still...
	{
		// we're stuffed as this ship can't stay still or move in the direction it wanted to!
		// this ship has been "squashed" by another ship.
		// This can happen for instance if:
		// This-ship has planned to move
		// Other-ship moves into the place that was expected to be only *formerly* occupied by this-ship
		// This-ship's move fails
		// ...now this-ship's current spot has been taken by other-ship and it can't move out of the way --- squashed!
		// In this case, the inoperable ship will be sunk immediately (effective in the future buffered time step).
		
		// cerr << "\nSea::attemptShipMoveAhead() - ";
		cerr << "Ship " << shipArrayIndex << " has been rammed and sailed onto rocks --- immediately sunk!";
		shipArrayBuffer[shipArrayIndex].applyDamage(shipArray[shipArrayIndex].getMaxDamage());
		return false;
	}

	else
	{
		// if we got here I am not sure why!?
		cleanQuit("ERORR: Sea::attemptShipMoveAhead() - unhandled return condition");
	}
	
	return false;	// keep compiler warning quiet. Should always return from the if-else statement above
}

// *NB a gold marker is left at a site, even if there is no gold left becuase it was collected earlier.
// Hence, a ship needs to keep track of the markers it has visited or risk wasting a turn trying to collect
// gold from a site it already knows to be empty.
bool Sea::attemptShipCollectGold(long shipArrayIndex)
{
	assert (goldArray!=NULL);
	bool goldCollectedFlag = false;
	
	long xIndex = shipArray[shipArrayIndex].getPositionX();
	long yIndex = shipArray[shipArrayIndex].getPositionY();
	
	if(cellArray[xIndex][yIndex].isOccupiedByGold())
	{
		long goldIndex = cellArray[xIndex][yIndex].getGoldIndex();	// the index of the gold in the current cell

		// collect the gold that is left at this site (if any), add reward to the ship's onboard store as kept in the shipArray *buffer*
		shipArrayBuffer[shipArrayIndex].addToRewardStored(goldArray[goldIndex].collectReward());
		goldCollectedFlag = true;
	}
	
	// Collecting gold involves staying stationary for the time step
	if (shipArray[shipArrayIndex].checkStationary())
	{
		// this call places the *buffer* copy of the ship in its new (same as the old) position into the *buffer* cell array
		shipArray[shipArrayIndex].makeStationary();
	}

	else
	{
		cleanQuit("ERROR: Sea::attemptShipCollectGold() - ship can't stay still to collect gold due to collision!");
	}

	return goldCollectedFlag;
}

void Sea::randomiseGoldPositions(void)
{
	bool suitableGridCellFound = false;
	long xIndex=0, yIndex=0;
	long goldAttemptCount = 0;
	long maxGoldAttemptCount = (cellArrayDimension * cellArrayDimension * 100);
	
	for (long a=0; a<numGolds; a++)
	{
		// randomly find a location to place this gold on the grid (assume uniform-random location distribution)
		// in the future could place golds in clusters, but not in this algorithm
		suitableGridCellFound = false;
		xIndex=0; yIndex=0;
		goldAttemptCount = 0;
		
		while ((!suitableGridCellFound) && (goldAttemptCount < maxGoldAttemptCount))
		{
			xIndex = lrandBetween(0, cellArrayDimension-1);
			yIndex = lrandBetween(0, cellArrayDimension-1);
	
			if ((!cellArray[xIndex][yIndex].isOccupiedByGold()) && (!cellArray[xIndex][yIndex].isOccupiedByRock()))
			{ suitableGridCellFound = true; }
			
			goldAttemptCount++;
		}
		
		if (suitableGridCellFound == false)
		{ cleanQuit("ERROR: (Sea::randomiseGoldPositions() Could not place gold after maxGoldAttemptCount attempts."); }
		
		else
		{
			goldArray[a].setPosition(xIndex, yIndex, 0);
			cellArray[xIndex][yIndex].setGoldPtr(&goldArray[a]);
			
			// Since the golds don't move, there is no need for a buffer to hold them
			// However, there is a cellArray and a cellArrayBuffer, both of which need their own
			// pointers to the one goldArray. Hence, we must set up the cellArrayBuffer with the
			// same pointers as we have in the cellArray
			cellArrayBuffer[xIndex][yIndex].setGoldPtr(&goldArray[a]);
		}
	}
}

bool Sea::placeGoldsInCellArrayAndCellArrayBuffer(void)
{
	long e,x,y;
	
	assert(cellArray!=0);
	assert(cellArrayBuffer!=0);
	assert(goldArray!=0);
	
	// for each element in the gold array....
	for (e=0; e<numGolds; e++)
    {
		x = goldArray[e].getPositionX();
		y = goldArray[e].getPositionY();
		
		if (cellArray[x][y].isOccupiedByGold() == false)
		{
			cellArray[x][y].setGoldPtr(&goldArray[e]);
		}
		
		else
		{
			cerr << "\nWARN: (Sea::placeGoldsInCellArray) failed attempt to place golds (gold list contains collisions in the cellArray)!";
			return false;
		}
		
		if (cellArrayBuffer[x][y].isOccupiedByGold() == false)
		{
			cellArrayBuffer[x][y].setGoldPtr(&goldArray[e]);
		}
		
		else
		{
			cerr << "\nWARN: (Sea::placeGoldsInCellArray) failed attempt to place golds (gold list contains collisions in the cellArrayBuffer)!";
			return false;
		}		
	}
	return true;
}

void Sea::display(void)
{
	displayRocksInSea();
	displayGoldsInSea();
	displayShipsInSea();
	displayWrecksInSea();
	displayCannonPath();
}

void Sea::displayShipsInSea(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArray[a][b].displayShip();
		}
	}
}

void Sea::displayRocksInSea(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArray[a][b].displayRock();
			
			if (a==0) // if this is a boundary rock, label its index for convenience
			{	cellArray[a][b].displayCellIndex(b); }
			
			else if (b==0)
			{	cellArray[a][b].displayCellIndex(a); }
		}
	}
}

void Sea::displayWrecksInSea(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArray[a][b].displayWreck();
		}
	}
}

void Sea::displayGoldsInSea(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArray[a][b].displayGold();
		}
	}
}

void Sea::displayCannonPath(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArray[a][b].displayCannonPath();
		}
	}
}


bool Sea::cellArrayBufferIsOccupiedByShip(long xIndex, long yIndex)
const
{
	return cellArrayBuffer[xIndex][yIndex].isOccupiedByShip();
}

bool Sea::cellArrayBufferIsOccupiedByRock(long xIndex, long yIndex)
const
{
	return cellArrayBuffer[xIndex][yIndex].isOccupiedByRock();
}

void Sea::clearCellArrayBuffer(void)
{
	for (long a=0; a<cellArrayDimension; a++)
	{
		for (long b=0; b<cellArrayDimension; b++)
		{
			cellArrayBuffer[a][b].clearShip();
			cellArrayBuffer[a][b].clearCannonBallPassedThrough();
		}
	}
}

void Sea::swapArrayBuffers(void)
{
    GridCell**	tmpGridCellArray;
    
    tmpGridCellArray  	= cellArrayBuffer;
    cellArrayBuffer		= cellArray;
    cellArray			= tmpGridCellArray;
	
	Ship* tmpShipArray;
	
	tmpShipArray		= shipArrayBuffer;
	shipArrayBuffer		= shipArray;
	shipArray			= tmpShipArray;
	
	// There is *no* goldArrayBuffer as they don't move between timesteps so one isn't needed.
	// Hence there is no goldBuffer array to switch.
}

bool Sea::cellArrayIsOccupiedByShip(long xIndex, long yIndex)
const
{
	return cellArray[xIndex][yIndex].isOccupiedByShip();
}

bool Sea::cellArrayIsOccupiedByRock(long xIndex, long yIndex)
const
{
	return cellArray[xIndex][yIndex].isOccupiedByRock();
}

bool Sea::cellArrayIsOccupiedByGoldMarker(long xIndex, long yIndex)
const
{
	return cellArray[xIndex][yIndex].isOccupiedByGold();
}

long Sea::getNeighbourIndex(long shipIndex, Globals::NeighbourDirection location)
{
    int neiX=0, neiY=0, curX=0, curY=0;
    int lastX = cellArrayDimension-1;
    int lastY = cellArrayDimension-1;
    
    curX = shipArray[shipIndex].getPositionX();
    curY = shipArray[shipIndex].getPositionY();
    
    switch (location)
    {
        case Globals::neighbourDown:		neiX=curX;		neiY=curY-1;	break;
        case Globals::neighbourUp:			neiX=curX;		neiY=curY+1;	break;
        case Globals::neighbourRight:		neiX=curX+1;	neiY=curY;		break;
        case Globals::neighbourLeft:		neiX=curX-1;	neiY=curY;		break;
		case Globals::neighbourEND_MARKER:	
			cleanQuit("\nERROR: (Sea::getNeighbourIndex) location=neighbourEND_MARKER, No neighbourIndex can exist!");
		break;
        default:
			cleanQuit("\nERROR: (Sea::getNeighbourIndex) fallen off switch!");
        break;
    }
    
    if (neiX<0)				{ neiX=lastX; }
    else if (neiX>lastX)	{ neiX=0; }
    
    if (neiY<0)				{ neiY=lastY; }
    else if (neiY>lastY)	{ neiY=0; }
    
    return cellArray[neiX][neiY].getShipId();
}

// routine to randomise the order of ship update by permuting the indices of the
// ships in the array and returning a new random one each time it is called but
// cycling through each one in turn before re-permuting.
long Sea::getRandomShipIndex(long randomIndexIndex)
{
	assert (randomIndexIndex < numShips);
	assert (randomIndexIndex >= 0);
	assert (randomShipIndexArray != NULL);
	
	return randomShipIndexArray[randomIndexIndex];
}

void Sea::shuffleRandomShipIndexArray(void)
{
	// to get a good shuffle, perform as
	// many swaps as there are ships in the sea
	long numSwaps = numShips;
	
	long tmpShipIndex, index1, index2, s;
	
	// simple algorithm to pick pairs of indices and swap around their contents
	for (s=0; s<numSwaps; s++)
	{
		index1 = lrandBetween(0, numShips-1);
		index2 = lrandBetween(0, numShips-1);
		
		tmpShipIndex = randomShipIndexArray[index1];
		randomShipIndexArray[index1] = randomShipIndexArray[index2];
		randomShipIndexArray[index2] = tmpShipIndex;
	}
}

long Sea::getCellArrayDimension(void)
const
{
	return cellArrayDimension;
}

double Sea::getCellDimension(void)
const
{
	return 1/(double)cellArrayDimension;
}

long Sea::wrapAroundIndexByDimension(long inIndex)
const
{
	long lastValidIndex = cellArrayDimension-1;
    long outIndex;
	
	if (inIndex<0)
	{ outIndex=cellArrayDimension + inIndex; }
    
	else if (inIndex > lastValidIndex)
	{ outIndex= inIndex-cellArrayDimension; }
	
	else
	{ outIndex = inIndex; }
	
	return outIndex;
}

SimpleVector Sea::getCellPosition(long xIndex, long yIndex)
const
{
	assert ((xIndex>=0) && (xIndex<cellArrayDimension));
	assert ((yIndex>=0) && (yIndex<cellArrayDimension));
	
	return cellArray[xIndex][yIndex].getPosition();
}

void Sea::debugOutput(ostream& outStream)	// human readable
{
	int a=0;
	
	for (a=0; a<numShips; a++)
	{
		shipArray[a].debugOutput(outStream);
	}
}

void Sea::fileInput(ifstream& inFile)		// machine readable
{
	long a, b;
	SimpleVector tmpPosition, tmpDimensions;
	
	// Check that the old pointers of the sea into which we are reading have shipn cleared out properly
	assert (cellArray==0);
	assert (cellArrayBuffer==0);
	assert (shipArray==0);
	assert (shipArrayBuffer==0);
	
	// Read cellArrayDimension, numShips...
	inFile >> cellArrayDimension;
	inFile >> numShips;
	
	cerr << "\n read cellArrayDimension: " << cellArrayDimension;
	cerr << "\n read numShips: " << numShips;
	
	assert (cellArrayDimension>0);
	if (numShips > (cellArrayDimension*cellArrayDimension))
	{ cleanQuit("\nERROR: (Sea::fileInput) newNumberShips > number of cells in the grid!"); }
	
	// Set up Sea data structures (e.g. shipArray)
	tmpDimensions.set(1/(double)cellArrayDimension, 1/(double)cellArrayDimension, 0);
	
	cellArray		= new GridCell* [cellArrayDimension];	// make an array of grid cells
	cellArrayBuffer	= new GridCell* [cellArrayDimension];	// make a buffer (for timestep t+1) of grid cells
	
	for (a=0; a<cellArrayDimension; a++)
	{
		cellArray[a]		= new GridCell [cellArrayDimension];	// make the grid cell array 2D
		cellArrayBuffer[a]	= new GridCell [cellArrayDimension];	// make the buffer grid cell array 2D
		
		for (b=0; b<cellArrayDimension; b++)						// set up the grid cells
		{															// don't set up the buffers. they'll be set up as the t+1 time step is calculated
			tmpPosition.set(a/(double)cellArrayDimension, b/(double)cellArrayDimension, 0);
			
			cellArray[a][b].setPosition(tmpPosition);
			cellArray[a][b].setDimensions(tmpDimensions);
			
			cellArrayBuffer[a][b].setPosition(tmpPosition);
			cellArrayBuffer[a][b].setDimensions(tmpDimensions);
		}
	}

	shipArray		= new Ship [numShips];						// new the array of Ships
	shipArrayBuffer	= new Ship [numShips];						// buffer to store the ship states at t+1	
	
	// Read in the ships in their saved positions and with their saved traits etc.
	for (a=0; a<numShips; a++)
	{	shipArray[a].fileInput(inFile);	}
	
	placeShipsInCellArray();
	
	// TO DO
	// read in gold types, placements and distribution values
	// read in be short term memory for approached golds
}

void Sea::fileOutput(ofstream& outFile)		// machine readable
{
	outFile << cellArrayDimension << "\n";
	outFile << numShips << "\n";
	
	for (int a=0; a<numShips; a++)
	{	shipArray[a].fileOutput(outFile);	}
	
	// TO DO
	// write out gold types, placements and distribution values
}

void Sea::computeAndOutputShipStatistics(long curFrame, bool mute)
{
	long b;
	double totalRewardStored=0;
	
	if (!mute)
	{
		cerr << "\n\n----------- time = " << curFrame;
	}
	
	for (b=0; b<numShips; b++)
	{
		totalRewardStored += shipArray[b].getRewardStored();
		
		if (!mute)
		{ shipArray[b].outputStatistics(curFrame); }
	}
	
	if (!mute)
	{
		cerr << "\nTotal reward stored on ships=";
		cerr << totalRewardStored << ", out of " << numGolds << " possible gold boxes.";

		cerr << "\nMean reward stored=" << (totalRewardStored / (double) numShips);
	}
}

void Sea::computeAndOutputShipStatisticsGameOver(long curFrame)
{
	long b=0, numWinners=0;
	long maxRewardFound=0, thisShipReward=0, totalRewardStored=0;
	
	cerr << "\n\n----------- GAME OVER ----------- ";
	
	// Find the total reward collected, and the maximum any ship collected
	for (b=0; b<numShips; b++)
	{
		thisShipReward = shipArray[b].getRewardStored();
		
		if (thisShipReward > maxRewardFound)
		{
			maxRewardFound = thisShipReward;
		}
		
		totalRewardStored += thisShipReward;
		shipArray[b].outputStatistics(curFrame);
	}
	
	// Now run through the list and report any ship that got the same amount
	// as the maximum amount of gold as a winner.
	
	cerr << "\n\n\n--- Ships with " << maxRewardFound << " gold boxes each ---";
	
	for (numWinners=0, b=0; b<numShips; b++)
	{
		if (shipArray[b].getRewardStored() == maxRewardFound)
		{
			numWinners++;
			shipArray[b].outputStatistics(curFrame);
		}
	}
	
	if (numWinners==1)
	{
		cerr << "\n--- Congratulations to the single winning ship! ---";
	}
	
	else // if (numWinners>1)
	{
		cerr << "\n--- Congratulations to the " << numWinners << " winning ships! ---";
	}
	
	cerr << "\nTotal reward stored on ships=";
	cerr << totalRewardStored << ", out of " << numGolds << " possible gold boxes.";
	cerr << " Mean reward stored=" << (totalRewardStored / (double) numShips);
	
	cerr << "\n\n--------------------------------- ";
}

void Sea::outputSeaStatistics(long numShips)
{
	cerr << "\n\nGAME PARAMETERS\n";
	cerr << "sea grid dimensions: " << cellArrayDimension << "\n";
	cerr << "number of ships: " << numShips << "\n";
	cerr << "number of gold boxes: " << numGolds << "\n";
}


